home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Tools / Development / MosaicSRC / libwww2 / SGML.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-03-13  |  16.3 KB  |  666 lines

  1. /*            General SGML Parser code        SGML.c
  2. **            ========================
  3. **
  4. **    This module implements an HTStream object. To parse an
  5. **    SGML file, create this object which is a parser. The object
  6. **    is (currently) created by being passed a DTD structure,
  7. **    and a target HTStructured oject at which to throw the parsed stuff.
  8. **    
  9. **     6 Feb 93  Binary seraches used. Intreface modified.
  10. */
  11. #include "SGML.h"
  12.  
  13. #include <ctype.h>
  14. #include <stdio.h>
  15. #include "HTUtils.h"
  16. #include "HTChunk.h"
  17.  
  18. #define INVALID (-1)
  19.  
  20. /*    The State (context) of the parser
  21. **
  22. **    This is passed with each call to make the parser reentrant
  23. **
  24. */
  25.  
  26. #define MAX_ATTRIBUTES 20    /* Max number of attributes per element */
  27.  
  28.     
  29. /*        Element Stack
  30. **        -------------
  31. **    This allows us to return down the stack reselcting styles.
  32. **    As we return, attribute values will be garbage in general.
  33. */
  34. typedef struct _HTElement HTElement;
  35. struct _HTElement {
  36.     HTElement *    next;    /* Previously nested element or 0 */
  37.     HTTag*        tag;    /* The tag at this level  */
  38. };
  39.  
  40.  
  41. /*    Internal Context Data Structure
  42. **    -------------------------------
  43. */
  44. struct _HTStream {
  45.  
  46.     CONST HTStreamClass *    isa;        /* inherited from HTStream */
  47.     
  48.     CONST SGML_dtd         *dtd;
  49.     HTStructuredClass    *actions;    /* target class  */
  50.     HTStructured    *target;    /* target object */
  51.  
  52.     HTTag         *current_tag;
  53.     int         current_attribute_number;
  54.     HTChunk        *string;
  55.     HTElement        *element_stack;
  56.     enum sgml_state { S_text, S_litteral, S_tag, S_tag_gap, 
  57.         S_attr, S_attr_gap, S_equals, S_value,
  58.         S_ero, S_cro,
  59.           S_squoted, S_dquoted, S_end, S_entity, S_junk_tag} state;
  60. #ifdef CALLERDATA          
  61.     void *        callerData;
  62. #endif
  63.     BOOL present[MAX_ATTRIBUTES];    /* Flags: attribute is present? */
  64.     char * value[MAX_ATTRIBUTES];    /* malloc'd strings or NULL if none */
  65. } ;
  66.  
  67.  
  68. #define PUTC(ch) ((*context->actions->put_character)(context->target, ch))
  69.  
  70.  
  71.  
  72. /*    Handle Attribute
  73. **    ----------------
  74. */
  75. /* PUBLIC CONST char * SGML_default = "";   ?? */
  76.  
  77. #ifdef __STDC__
  78. PRIVATE void handle_attribute_name(HTStream * context, char * s)
  79. #else
  80. PRIVATE void handle_attribute_name(context, s)
  81.     HTStream * context;
  82.     char *s;
  83. #endif
  84. {
  85.  
  86.     HTTag * tag = context->current_tag;
  87.     attr* attributes = tag->attributes;
  88.  
  89.     int high, low, i, diff;        /* Binary search for attribute name */
  90.     for(low=0, high=tag->number_of_attributes;
  91.             high > low ;
  92.         diff < 0 ? (low = i+1) : (high = i) )  {
  93.     i = (low + (high-low)/2);
  94.     diff = strcasecomp(attributes[i].name, s);
  95.     if (diff==0) {            /* success: found it */
  96.             context->current_attribute_number = i;
  97.         context->present[i] = YES;
  98.         if (context->value[i]) {
  99.         free(context->value[i]);
  100.         context->value[i] = NULL;
  101.         }
  102.         return;
  103.     } /* if */
  104.     
  105.     } /* for */
  106.     
  107.     if (TRACE)
  108.     fprintf(stderr, "SGML: Unknown attribute %s for tag %s\n",
  109.         s, context->current_tag->name);
  110.     context->current_attribute_number = INVALID;    /* Invalid */
  111. }
  112.  
  113.  
  114. /*    Handle attribute value
  115. **    ----------------------
  116. */
  117. #ifdef __STDC__
  118. PRIVATE void handle_attribute_value(HTStream * context, char * s)
  119. #else
  120. PRIVATE void handle_attribute_value(context, s)
  121.     HTStream * context;
  122.     char *s;
  123. #endif
  124. {
  125.     if (context->current_attribute_number != INVALID) {
  126.     StrAllocCopy(context->value[context->current_attribute_number], s);
  127.     } else {
  128.         if (TRACE) fprintf(stderr, "SGML: Attribute value %s ignored\n", s);
  129.     }
  130.     context->current_attribute_number = INVALID; /* can't have two assignments! */
  131. }
  132.  
  133.  
  134. /*    Handle entity
  135. **    -------------
  136. **
  137. ** On entry,
  138. **    s    contains the entity name zero terminated
  139. ** Bugs:
  140. **    If the entity name is unknown, the terminator is treated as
  141. **    a printable non-special character in all cases, even if it is '<'
  142. */
  143. #ifdef __STDC__
  144. PRIVATE void handle_entity(HTStream * context, char term)
  145. #else
  146. PRIVATE void handle_entity(context, term)
  147.     HTStream * context;
  148.     char term;
  149. #endif
  150. {
  151.  
  152.     CONST char ** entities = context->dtd->entity_names;
  153.     CONST char *s = context->string->data;
  154.     
  155.     int high, low, i, diff;
  156.     for(low=0, high = context->dtd->number_of_entities;
  157.             high > low ;
  158.         diff < 0 ? (low = i+1) : (high = i))   {  /* Binary serach */
  159.     i = (low + (high-low)/2);
  160.     diff = strcmp(entities[i], s);    /* Csse sensitive! */
  161.     if (diff==0) {            /* success: found it */
  162.         (*context->actions->put_entity)(context->target, i);
  163.         return;
  164.     }
  165.     }
  166.     /* If entity string not found, display as text */
  167.     if (TRACE)
  168.     fprintf(stderr, "SGML: Unknown entity %s\n", s); 
  169.     PUTC('&');
  170.     {
  171.     CONST char *p;
  172.     for (p=s; *p; p++) {
  173.         PUTC(*p);
  174.     }
  175.     }
  176.     PUTC(term);
  177. }
  178.  
  179.  
  180. /*    End element
  181. **    -----------
  182. */
  183. #ifdef __STDC__
  184. PRIVATE void end_element(HTStream * context, HTTag * old_tag)
  185. #else
  186. PRIVATE void end_element(context, old_tag)
  187.     HTTag * old_tag;
  188.     HTStream * context;
  189. #endif
  190. {
  191.     if (TRACE) fprintf(stderr, "SGML: End   </%s>\n", old_tag->name);
  192.     if (old_tag->contents == SGML_EMPTY) {
  193.         if (TRACE) fprintf(stderr,"SGML: Illegal end tag </%s> found.\n",
  194.         old_tag->name);
  195.     return;
  196.     }
  197.     while (context->element_stack)     {/* Loop is error path only */
  198.     HTElement * N = context->element_stack;
  199.     HTTag * t = N->tag;
  200.     
  201.     if (old_tag != t) {        /* Mismatch: syntax error */
  202.         if (context->element_stack->next) {    /* This is not the last level */
  203.         if (TRACE) fprintf(stderr,
  204.             "SGML: Found </%s> when expecting </%s>. </%s> assumed.\n",
  205.             old_tag->name, t->name, t->name);
  206.         } else {            /* last level */
  207.         if (TRACE) fprintf(stderr,
  208.                 "SGML: Found </%s> when expecting </%s>. </%s> Ignored.\n",
  209.             old_tag->name, t->name, old_tag->name);
  210.             return;            /* Ignore */
  211.         }
  212.     }
  213.     
  214.     context->element_stack = N->next;        /* Remove from stack */
  215.     free(N);
  216.     (*context->actions->end_element)(context->target,
  217.          t - context->dtd->tags);
  218.     if (old_tag == t) return;  /* Correct sequence */
  219.     
  220.     /* Syntax error path only */
  221.     
  222.     }
  223.     if (TRACE) fprintf(stderr,
  224.     "SGML: Extra end tag </%s> found and ignored.\n", old_tag->name);
  225. }
  226.  
  227.  
  228. /*    Start a element
  229. */
  230. #ifdef __STDC__
  231. PRIVATE void start_element(HTStream * context)
  232. #else
  233. PRIVATE void start_element(context)
  234.     HTStream * context;
  235. #endif
  236. {
  237.     HTTag * new_tag = context->current_tag;
  238.     
  239.     if (TRACE) fprintf(stderr, "SGML: Start <%s>\n", new_tag->name);
  240.     (*context->actions->start_element)(
  241.         context->target,
  242.     new_tag - context->dtd->tags,
  243.     context->present,
  244.     (CONST char**) context->value);  /* coerce type for think c */
  245.     if (new_tag->contents != SGML_EMPTY) {        /* i.e. tag not empty */
  246.     HTElement * N = (HTElement *)malloc(sizeof(HTElement));
  247.         if (N == NULL) outofmem(__FILE__, "start_element");
  248.     N->next = context->element_stack;
  249.     N->tag = new_tag;
  250.     context->element_stack = N;
  251.     }
  252. }
  253.  
  254.  
  255. /*        Find Tag in DTD tag list
  256. **        ------------------------
  257. **
  258. ** On entry,
  259. **    dtd    points to dtd structire including valid tag list
  260. **    string    points to name of tag in question
  261. **
  262. ** On exit,
  263. **    returns:
  264. **        NULL        tag not found
  265. **        else        address of tag structure in dtd
  266. */
  267. PRIVATE HTTag * find_tag ARGS2(CONST SGML_dtd*, dtd, char *, string)
  268. {
  269.     int high, low, i, diff;
  270.     for(low=0, high=dtd->number_of_tags;
  271.             high > low ;
  272.         diff < 0 ? (low = i+1) : (high = i))   {  /* Binary serach */
  273.     i = (low + (high-low)/2);
  274.     diff = strcasecomp(dtd->tags[i].name, string);    /* Case insensitive */
  275.     if (diff==0) {            /* success: found it */
  276.         return &dtd->tags[i];
  277.     }
  278.     }
  279.     return NULL;
  280. }
  281.  
  282. /*________________________________________________________________________
  283. **            Public Methods
  284. */
  285.  
  286.  
  287. PUBLIC void SGML_end  ARGS1(HTStream *, context)
  288. {
  289. /*    Could check that we are back to bottom of stack! @@  */
  290.  
  291.     (*context->actions->end_document)(context->target);
  292. }
  293.  
  294.  
  295. PUBLIC void SGML_free  ARGS1(HTStream *, context)
  296. {
  297.     (*context->actions->free)(context->target);
  298.     HTChunkFree(context->string);
  299.     free(context);
  300. }
  301.  
  302.  
  303. /*    Read and write user callback handle
  304. **    -----------------------------------
  305. **
  306. **   The callbacks from the SGML parser have an SGML context parameter.
  307. **   These calls allow the caller to associate his own context with a
  308. **   particular SGML context.
  309. */
  310.  
  311. #ifdef CALLERDATA          
  312. PUBLIC void* SGML_callerData ARGS1(HTStream *, context)
  313. {
  314.     return context->callerData;
  315. }
  316.  
  317. PUBLIC void SGML_setCallerData ARGS2(HTStream *, context, void*, data)
  318. {
  319.     context->callerData = data;
  320. }
  321. #endif
  322.  
  323. PUBLIC void SGML_character ARGS2(HTStream *, context, char,c)
  324.  
  325. {
  326.     CONST SGML_dtd    *dtd    =    context->dtd;
  327.     HTChunk    *string =     context->string;
  328.  
  329.     switch(context->state) {
  330.     case S_text:
  331.     if (c=='&' && (!context->element_stack || (
  332.                  context->element_stack->tag  &&
  333.                  ( context->element_stack->tag->contents == SGML_MIXED
  334.                || context->element_stack->tag->contents ==
  335.                                    SGML_RCDATA)
  336.             ))) {
  337.         string->size = 0;
  338.         context->state = S_ero;
  339.         
  340.     } else if (c=='<') {
  341.         string->size = 0;
  342.         context->state = (context->element_stack &&
  343.                 context->element_stack->tag  &&
  344.                 context->element_stack->tag->contents == SGML_LITTERAL) ?
  345.                     S_litteral : S_tag;
  346.     } else PUTC(c);
  347.     break;
  348.  
  349. /*    In litteral mode, waits only for specific end tag!
  350. **    Only foir compatibility with old servers.
  351. */
  352.     case S_litteral :
  353.     HTChunkPutc(string, c);
  354.     if ( TOUPPER(c) != ((string->size ==1) ? '/'
  355.         : context->element_stack->tag->name[string->size-2])) {
  356.         int i;
  357.         
  358.         /*    If complete match, end litteral */
  359.         if ((c=='>') && (!context->element_stack->tag->name[string->size-2])) {
  360.         end_element(context, context->element_stack->tag);
  361.         string->size = 0;
  362.         context->current_attribute_number = INVALID;
  363.         context->state = S_text;
  364.         break;
  365.         }        /* If Mismatch: recover string. */
  366.         PUTC( '<');
  367.         for (i=0; i<string->size; i++)    /* recover */
  368.            PUTC(
  369.                                  string->data[i]);
  370.         context->state = S_text;    
  371.     }
  372.     
  373.         break;
  374.  
  375. /*    Character reference or Entity
  376. */
  377.    case S_ero:
  378.        if (c=='#') {
  379.         context->state = S_cro;  /*   &# is Char Ref Open */ 
  380.         break;
  381.     }
  382.     context->state = S_entity;    /* Fall through! */
  383.     
  384. /*    Handle Entities
  385. */
  386.     case S_entity:
  387.     if (isalnum(c))
  388.         HTChunkPutc(string, c);
  389.     else {
  390.         HTChunkTerminate(string);
  391.         handle_entity(context, c);
  392.         context->state = S_text;
  393.     }
  394.     break;
  395.  
  396. /*    Character reference
  397. */
  398.     case S_cro:
  399.     if (isalnum(c))
  400.         HTChunkPutc(string, c);    /* accumulate a character NUMBER */
  401.     else {
  402.         int value;
  403.         HTChunkTerminate(string);
  404.         if (sscanf(string->data, "%d", &value)==1)
  405.             PUTC((char)value);
  406.         context->state = S_text;
  407.     }
  408.     break;
  409.  
  410. /*        Tag
  411. */        
  412.     case S_tag:                /* new tag */
  413.     if (isalnum(c))
  414.         HTChunkPutc(string, c);
  415.     else {                /* End of tag name */
  416.         HTTag * t;
  417.         if (c=='/') {
  418.         if (TRACE) if (string->size!=0)
  419.             fprintf(stderr,"SGML:  `<%s/' found!\n", string->data);
  420.         context->state = S_end;
  421.         break;
  422.         }
  423.         HTChunkTerminate(string) ;
  424.  
  425.         t = find_tag(dtd, string->data);
  426.         if (!t) {
  427.         if(TRACE) fprintf(stderr, "SGML: *** Unknown element %s\n",
  428.             string->data);
  429.         context->state = (c=='>') ? S_text : S_junk_tag;
  430.         break;
  431.         }
  432.         context->current_tag = t;
  433.         
  434.         /*  Clear out attributes
  435.         */
  436.         
  437.         {
  438.             int i;
  439.             for (i=0; i< context->current_tag->number_of_attributes; i++)
  440.                 context->present[i] = NO;
  441.         }
  442.         string->size = 0;
  443.         context->current_attribute_number = INVALID;
  444.         
  445.         if (c=='>') {
  446.         if (context->current_tag->name) start_element(context);
  447.         context->state = S_text;
  448.         } else {
  449.             context->state = S_tag_gap;
  450.         }
  451.     }
  452.     break;
  453.  
  454.         
  455.     case S_tag_gap:        /* Expecting attribute or > */
  456.     if (WHITE(c)) break;    /* Gap between attributes */
  457.     if (c=='>') {        /* End of tag */
  458.         if (context->current_tag->name) start_element(context);
  459.         context->state = S_text;
  460.         break;
  461.     }
  462.     HTChunkPutc(string, c);
  463.     context->state = S_attr;        /* Get attribute */
  464.     break;
  465.     
  466.                    /* accumulating value */
  467.     case S_attr:
  468.     if (WHITE(c) || (c=='>') || (c=='=')) {        /* End of word */
  469.         HTChunkTerminate(string) ;
  470.         handle_attribute_name(context, string->data);
  471.         string->size = 0;
  472.         if (c=='>') {        /* End of tag */
  473.         if (context->current_tag->name) start_element(context);
  474.         context->state = S_text;
  475.         break;
  476.         }
  477.         context->state = (c=='=' ?  S_equals: S_attr_gap);
  478.     } else {
  479.         HTChunkPutc(string, c);
  480.     }
  481.     break;
  482.         
  483.     case S_attr_gap:        /* Expecting attribute or = or > */
  484.     if (WHITE(c)) break;    /* Gap after attribute */
  485.     if (c=='>') {        /* End of tag */
  486.         if (context->current_tag->name) start_element(context);
  487.         context->state = S_text;
  488.         break;
  489.     } else if (c=='=') {
  490.         context->state = S_equals;
  491.         break;
  492.     }
  493.     HTChunkPutc(string, c);
  494.     context->state = S_attr;        /* Get next attribute */
  495.     break;
  496.     
  497.     case S_equals:            /* After attr = */ 
  498.     if (WHITE(c)) break;    /* Before attribute value */
  499.     if (c=='>') {        /* End of tag */
  500.         if (TRACE) fprintf(stderr, "SGML: found = but no value\n");
  501.         if (context->current_tag->name) start_element(context);
  502.         context->state = S_text;
  503.         break;
  504.         
  505.     } else if (c=='\'') {
  506.         context->state = S_squoted;
  507.         break;
  508.  
  509.     } else if (c=='"') {
  510.         context->state = S_dquoted;
  511.         break;
  512.     }
  513.     HTChunkPutc(string, c);
  514.     context->state = S_value;
  515.     break;
  516.     
  517.     case S_value:
  518.     if (WHITE(c) || (c=='>')) {        /* End of word */
  519.         HTChunkTerminate(string) ;
  520.         handle_attribute_value(context, string->data);
  521.         string->size = 0;
  522.         if (c=='>') {        /* End of tag */
  523.         if (context->current_tag->name) start_element(context);
  524.         context->state = S_text;
  525.         break;
  526.         }
  527.         else context->state = S_tag_gap;
  528.     } else {
  529.         HTChunkPutc(string, c);
  530.     }
  531.     break;
  532.         
  533.     case S_squoted:        /* Quoted attribute value */
  534.     if (c=='\'') {        /* End of attribute value */
  535.         HTChunkTerminate(string) ;
  536.         handle_attribute_value(context, string->data);
  537.         string->size = 0;
  538.         context->state = S_tag_gap;
  539.     } else {
  540.         HTChunkPutc(string, c);
  541.     }
  542.     break;
  543.     
  544.     case S_dquoted:        /* Quoted attribute value */
  545.     if (c=='"') {        /* End of attribute value */
  546.         HTChunkTerminate(string) ;
  547.         handle_attribute_value(context, string->data);
  548.         string->size = 0;
  549.         context->state = S_tag_gap;
  550.     } else {
  551.         HTChunkPutc(string, c);
  552.     }
  553.     break;
  554.     
  555.     case S_end:                    /* </ */
  556.     if (isalnum(c))
  557.         HTChunkPutc(string, c);
  558.     else {                /* End of end tag name */
  559.         HTTag * t;
  560.         HTChunkTerminate(string) ;
  561.         if (!*string->data)    {    /* Empty end tag */
  562.             t = context->element_stack->tag;
  563.         } else {
  564.         t = find_tag(dtd, string->data);
  565.         }
  566.         if (!t) {
  567.         if(TRACE) fprintf(stderr,
  568.             "Unknown end tag </%s>\n", string->data); 
  569.         } else {
  570.             context->current_tag = t;
  571.         end_element( context, context->current_tag);
  572.         }
  573.  
  574.         string->size = 0;
  575.         context->current_attribute_number = INVALID;
  576.         if (c!='>') {
  577.         if (TRACE && !WHITE(c))
  578.             fprintf(stderr,"SGML:  `</%s%c' found!\n",
  579.                 string->data, c);
  580.         context->state = S_junk_tag;
  581.         } else {
  582.             context->state = S_text;
  583.         }
  584.     }
  585.     break;
  586.  
  587.         
  588.     case S_junk_tag:
  589.     if (c=='>') {
  590.         context->state = S_text;
  591.     }
  592.     
  593.     } /* switch on context->state */
  594.  
  595. }  /* SGML_character */
  596.  
  597.  
  598. PUBLIC void SGML_string ARGS2(HTStream *, context, CONST char*, str)
  599. {
  600.     CONST char *p;
  601.     for(p=str; *p; p++)
  602.         SGML_character(context, *p);
  603. }
  604.  
  605.  
  606. PUBLIC void SGML_write ARGS3(HTStream *, context, CONST char*, str, int, l)
  607. {
  608.     CONST char *p;
  609.     CONST char *e = str+l;
  610.     for(p=str; p<e; p++)
  611.         SGML_character(context, *p);
  612. }
  613.  
  614. /*_______________________________________________________________________
  615. */
  616.  
  617. PRIVATE void SGML_handle_interrupt  ARGS1(HTStream *, context)
  618. {
  619. }
  620.  
  621. /*    Structured Object Class
  622. **    -----------------------
  623. */
  624. PUBLIC CONST HTStreamClass SGMLParser = 
  625. {        
  626.     "SGMLParser",
  627.     SGML_free,
  628.     SGML_end,
  629.     SGML_character,     SGML_string,  SGML_write,
  630.         SGML_handle_interrupt
  631. }; 
  632.  
  633. /*    Create SGML Engine
  634. **    ------------------
  635. **
  636. ** On entry,
  637. **    dtd        represents the DTD, along with
  638. **    actions        is the sink for the data as a set of routines.
  639. **
  640. */
  641.  
  642. PUBLIC HTStream* SGML_new  ARGS2(
  643.     CONST SGML_dtd *,    dtd,
  644.     HTStructured *,        target)
  645. {
  646.     int i;
  647.     HTStream* context = (HTStream *) malloc(sizeof(*context));
  648.     if (!context) outofmem(__FILE__, "SGML_begin");
  649.  
  650.     context->isa = &SGMLParser;
  651.     context->string = HTChunkCreate(128);    /* Grow by this much */
  652.     context->dtd = dtd;
  653.     context->target = target;
  654.     context->actions = (HTStructuredClass*)(((HTStream*)target)->isa);
  655.                         /* Ugh: no OO */
  656.     context->state = S_text;
  657.     context->element_stack = 0;            /* empty */
  658. #ifdef CALLERDATA          
  659.     context->callerData = (void*) callerData;
  660. #endif    
  661.     for(i=0; i<MAX_ATTRIBUTES; i++) context->value[i] = 0;
  662.  
  663.     return context;
  664. }
  665.  
  666.